<!--
Date: 2025-08-13
Copyright (c) 2025 Industrial Software Feature Database
-->
<template>
  <div class="upload_elements">
    <div class="steps">
      <el-steps :active="active" style="width: 100%" align-center>
        <el-step title="文件上传" />
        <el-step title="数据的处理" />
        <!-- <el-step title="数据的转换" /> -->
        <el-step title="完成" />
      </el-steps>
    </div>
    <div class="upload" v-show="active === 1" v-loading="uploadLoading" element-loading-text="数据处理中...">
      <div class="upload_content">
        <el-form size="small" label-width="120">
          <el-form-item label="所属机型：" required>
            <el-select style="width: 480px" v-model="uploadForm.modelNumber" clearable placeholder="请选择机型" @change="modelChange">
              <el-option v-for="item in modelNumberList" :label="item.name" :value="item.mark" :key="item.mark"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="所属批次：" required>
            <el-select v-if="!uploadForm.modelNumber" style="width: 480px" v-model="uploadForm.batchNumber" filterable clearable placeholder="请选择批次号">
              <el-option v-for="(item, index) in batchList" :label="batchList[index]" :value="batchList[index]" :key="index"></el-option>
            </el-select>
            <el-autocomplete
              v-else
              style="width: 480px"
              v-model="uploadForm.batchNumber"
              :fetch-suggestions="querySearch"
              clearable
              class="inline-input w-50"
              placeholder="请输入批次号"
            />
          </el-form-item>
          <el-form-item label="结果轮次：" required>
            <el-select v-if="!uploadForm.modelNumber" style="width: 480px" v-model="uploadForm.roundNumber" clearable placeholder="请选择结果轮次">
              <el-option v-for="item in roundList" :label="item.name" :value="item.mark" :key="item.mark"></el-option>
            </el-select>
            <el-autocomplete
              v-else
              style="width: 480px"
              v-model="uploadForm.roundNumber"
              :fetch-suggestions="querySearch1"
              clearable
              class="inline-input w-50"
              placeholder="请输入结果轮次"
            />
          </el-form-item>
          <!-- <el-form-item v-if="props.pageType === 'importComp'" label="部段：" required>
            <el-select style="width: 480px" v-model="uploadForm.sectionNumber" clearable placeholder="请选择部段号">
              <el-option v-for="item in sectionList" :label="item.name + '[' + item.mark + ']'" :value="item.mark" :key="item.mark"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item v-if="props.pageType !== 'importComp'" label="索引文件名：" >
            <el-input style="width: 480px" v-model="uploadForm.indexFileName" placeholder="请输入索引文件名，如：C919_10002_D20200910"/>
          </el-form-item> -->
          <el-form-item label="文件上传：" required>
            <el-upload 
              class="upload-demo" 
              style="width:480px;" 
              multiple 
              drag 
              :file-list="uploadFiles" 
              :beforeUpload="beforeUpload" 
              :on-change="handleChange"
              :on-success="handleSuccess"
              :on-error="handleError"
              :http-request="componentImport"
              accept=".op2,.h5">
              <el-icon class="el-icon--upload"><upload-filled /></el-icon>
              <div class="el-upload__text" style="text-align: center;">
                点击或将文件拖拽到这里上传<br>
                1.只支持.op2,.h5文件上传。<br>
                2.支持多文件同时上传。<br>
              </div>
            </el-upload>
          </el-form-item>
          <el-form-item v-if="uploadForm.modelNumber && sectionList.length > 0" label="适用部段：" required>
            <el-checkbox-group style="width:480px;"  v-model="uploadForm.sectionNumbers">
              <el-checkbox v-for="item in sectionList" :label="item.mark" :key="item.mark">{{ item.name }}</el-checkbox>
            </el-checkbox-group>
          </el-form-item>
          <el-form-item label="备注：">
            <el-input type="textarea" style="width:480px;" rows="2" v-model="uploadForm.remarks"></el-input>
          </el-form-item>
        </el-form>
      </div>
    </div>
    <div v-loading="uploadLoading" element-loading-text="确认上传中..." class="upload_check" v-show="active === 2">
      <ResolvedData ref="resolvedDataRef" />
    </div>
    <!-- <div class="upload_list" v-show="active === 3" v-loading="uploadLoading" element-loading-text="数据导入中...">
      <DataTransform :batchNumber="uploadForm.batchNumber" :data="zipData" v-if="active === 3" :active="active" @change="dataChange" />
    </div> -->
    <div class="upload_success" v-show="active === 3">
      <div class="upload_success_message">
        <el-icon size="48" color="#67C23A"><CircleCheck /></el-icon>
        <h3 class="res-statistic">
          本次共上传文件 <el-link type="primary">{{ files.length }}</el-link> 笔，
          <template v-if="notFilterCount > 0">其中通过数据质量检测的有 <el-link type="success">{{ notFilterCount }}</el-link> 条；</template>
          <template v-if="filterCount > 0">未通过数据质量检测的有 <el-link type="danger">{{ filterCount }}</el-link> 条。具体如下：</template>
        </h3>
      </div>
    </div>
    <div class="option_btn">
      <el-button size="small" @click="cancel" v-show="active === 1" :disabled="uploadLoading">取消</el-button>
      <el-button size="small" type="primary" :disabled="uploadLoading" v-show="active === 1" @click="step('next', 2)">下一步</el-button>
      <!-- <el-button size="small" type="primary" @click="exportExcel" v-show="active === 2">导出数据</el-button> -->
      <el-button size="small" :disabled="uploadLoading" v-show="active === 2" @click="step('pre', 1)">返回上一步</el-button>
      <el-button size="small" type="primary" :disabled="uploadLoading" v-show="active === 2" @click="comfirmImport">确认上传</el-button>
      <el-button size="small" type="primary" v-show="active === 3" @click="finish">完成</el-button>
    </div>
    <el-dialog v-model="dialogFormVisible" title="文件努力上传中，请耐心等待..." width="500" :show-close="false">
      <div class="upload-list">
        <div 
          v-for="file in files"
          :key="file.fileHash"
          class="file-item"
        >
          <div class="file-info">
            <span class="filename">{{ file.fileName }}</span>
            <span class="size">{{ formatSize(file.fileSize) }}</span>
          </div>
          
          <div class="progress-container">
            <div 
              class="progress-bar" 
              :style="{ width: `${file.progress}%` }"
            ></div>
            {{ file.progress }}
            <span class="progress-text">{{ Math.floor(file.progress) }}%</span>
          </div>
        </div>
      </div>
    </el-dialog>
  </div>
</template>
<script  setup name="ImportModelData">
import { getToken } from '@/utils/auth';
import ResolvedData from './ResolvedData.vue';
import { getBatchNo, getRoundNo, getMeshModelStructureList, confirmOp2Upload, handleOp2UploadData  } from '@/api/dataAcquisition';
import { calculateHash } from './generateHash.js';
const CHUNK_SIZE = 10 * 1024 * 1024; // 切片
const THREAD_COUNT = 8; //navigator.hardwareConcurrency || 4;
const files = ref([]) // 响应式状态
const emits = defineEmits(['handleEvent']);
const props = defineProps(['tableName']);
const uploadLoading = ref(false);
const active = ref(1);
const resolvedDataRef = ref(null);
const sectionMap = ref({})
const uploadForm = ref({
  sectionNumbers: [],
  modelNumber: undefined,
  roundNumber: undefined,
  remarks: '',
  modelNumber: '',
  filtered: 0,
  qualityCheck: 0,
  tableName: '985eb407-0e77-4168-8b6b-7bf0fd0f3e91'
})
const finalFileSuffix = ref('');
const modelNumberList = ref([]);
const batchList = ref([]);
const roundList = ref([]);
const sectionList = ref([]);
const uploadFiles = ref([]);
const dialogFormVisible = ref(false);
// 导入确认数据
const confirmImportTableData = ref([]);
const handleModelData = ref(null);
const uploadResult = ref(null); // 上传结果
const filterCount = ref(0);
const notFilterCount = ref(0);
const totalCount = ref(0);
const treeVo = ref([]);
const fileUploadResult = ref({});
// 工具函数：格式化文件大小
const formatSize = (bytes) => {
  const units = ['B', 'KB', 'MB', 'GB']
  let size = bytes
  let unitIndex = 0
  while (size >= 1024 && unitIndex < units.length - 1) {
    size /= 1024
    unitIndex++
  }
  return `${size.toFixed(1)} ${units[unitIndex]}`
}

async function generateSHA256(file) {
  const buffer = await file.arrayBuffer()
  const hashBuffer = await crypto.subtle.digest('SHA-256', buffer)
  const hashArray = Array.from(new Uint8Array(hashBuffer))
  return hashArray.map(b => b.toString(16).padStart(2, '0')).join('')
}

// 生成文件哈希（使用 Worker）
// const generateFileHash = async (file) => {
//   const worker = new Worker(new URL('./hash.worker.js', import.meta.url), {
//     type: 'module'
//   })
  
//   return new Promise((resolve) => {
//     worker.postMessage({ file })
//     worker.onmessage = (e) => {
//       worker.terminate();
//       resolve(e.data)
//     }
//   })
// }

// 获取机型
const getModelList = () => {
  getMeshModelStructureList({parentId: 0, pageSize: 999}).then(res => {
    if (res) {
      modelNumberList.value = res.rows;
    }
  })
}
// 获取部段
const getSectionList = (id) => {
  getMeshModelStructureList({parentId: id, pageSize: 999, filterSection: true}).then(res => {
    if (res) {
      sectionList.value = res.rows;
      res.rows.forEach(item => {
        sectionMap.value[item.mark] = item.name;
      })
    }
  })
}
// 获取批次号 && 轮次号
const modelChange = (val) => {
  if (val) {
    uploadForm.value.batchNumber = '';
    uploadForm.value.roundNumber = '';
    uploadForm.value.sectionNumbers = [];
    getBatchNo({modelNumber: val}).then(res => {
      if (res) {    
        batchList.value = res.data.map(name => Object.fromEntries([['value', name]]))
      }
    })
    getRoundNo({modelNumber: val}).then(res => {
      if (res) {    
        roundList.value = res.data.map(name => Object.fromEntries([['value', name]]))
      }
    })
    let _id = modelNumberList.value.find(item => item.mark == val)?.id;
    _id && getSectionList(_id);
  }
}
const querySearch = (queryString, cb) => {
  const results = queryString
    ? batchList.value.filter(item => item.value.toLowerCase().indexOf(queryString.toLowerCase()) > -1)
    : batchList.value
  cb(results)
}
const querySearch1 = (queryString, cb) => {
  const results = queryString
    ? roundList.value.filter(item => item.value.toLowerCase().indexOf(queryString.toLowerCase()) > -1)
    : roundList.value
  cb(results)
}

// 文件状态改变时的钩子，添加文件、上传成功和上传失败时都会被调用
const handleChange = (file, fileList) => {
  // console.log('文件状态改变', file, fileList);
  uploadFiles.value = fileList;
};

// 文件上传成功时的钩子
const handleSuccess = (response, file, fileList) => {
  // console.log('文件上传成功', response, file, fileList);
};
// 文件上传失败时的钩子
const handleError = (error, file, fileList) => {
  // console.log('文件上传失败', error, file, fileList);
};
// 验证文件格式是否正确
const beforeUpload = (file) => {
  // let isFile = file.name.split('.')[file.name.split('.').length - 1] == 'op2';
  let fileSuffix = file.name.split('.')[file.name.split('.').length - 1];
  finalFileSuffix.value = fileSuffix;
  let isFile = fileSuffix == 'op2' || fileSuffix == 'h5';
  // const isLt = file.size / 1024 / 1024 < 500;
  if (!isFile) {
    ElMessage.error('导入文件格式不正确');
    return false;
  }
  // if (!isLt) {
  //   ElMessage.error(`导入文件太大，请交由系统管理员进行处理`);
  //   return false;
  // }
  //清空filelista
  // uploadFiles.value = [];
  return isFile;
};
// 读取压缩文件
const componentImport = async (file, fileList) => {
  // console.log(file, fileList)
  // uploadFiles.value.push(file.file);
};

const step = async (type) => {
  if (type === 'next') {
    if (active.value === 1) {
      if (!uploadForm.value.modelNumber) {
        ElMessage({ showClose: true, message: '请选择机型', type: 'error' });
        return;
      }
      // if (!uploadForm.value.roundNumber) {
      //   ElMessage({ showClose: true, message: '请选择结果轮次', type: 'error' });
      //   return;
      // }
      if (!uploadForm.value.batchNumber) {
        ElMessage({ showClose: true, message: '请选择批次', type: 'error' });
        return;
      }
      if (!uploadForm.value.sectionNumbers || uploadForm.value.sectionNumbers.length == 0) {
        ElMessage({ showClose: true, message: '请选择适用部段', type: 'error' });
        return;
      }
      if (!uploadFiles.value || uploadFiles.value.length == 0) {
        ElMessage({ showClose: true, message: '请先上传文件', type: 'error' });
        return;
      }
      // op2文件上传
      uploadLoading.value = true;
      let paramsArr = [];
      files.value.length = 0;
      let params = JSON.parse(JSON.stringify(uploadForm.value))
      params.sectionNumbers = params.sectionNumbers.join(',')
      for (const file of uploadFiles.value) {
        const isfdLargeFileBo = {
          fileName: file.name,
          fileSize: file.size,
          chunkSize: CHUNK_SIZE,
          progress: 0,
          "fileSuffix": finalFileSuffix.value,
          file: file
        }
        files.value.push(isfdLargeFileBo);
        paramsArr.push({
          ...params,
          isfdLargeFileBo
        })
        try {
          let fileHash = await calculateHash(file.raw, CHUNK_SIZE);
          isfdLargeFileBo.fileHash = fileHash;
        } catch (err) {
          console.error('文件处理失败:', err)
          // removeFile(fileData)
        }
      }
      handleOp2UploadData(paramsArr).then(res => {
        if (res) {
          uploadLoading.value = false;
          fileUploadResult.value = res.data;
          active.value ++;
          uploadForm.value.sectionNumbersName = uploadForm.value.sectionNumbers.map(item => sectionMap.value[item]).join(',')
          resolvedDataRef.value.handleData(res.data, files.value, uploadForm.value);
        }
      }).finally(() => {
        uploadLoading.value = false;
      })
      return;
    }
    if (active.value === 2) {
      comfirmImport()
    }
    if (active.value === 3) {
    }
   
  } else {
    active.value--;
  }
};
// 移除文件
const removeFile = (fileData) => {
  const index = files.value.findIndex(f => f.id === fileData.id)
  if (index !== -1) {
    files.value.splice(index, 1)
  }
}
// 确认上传
const comfirmImport = () => {
  if (resolvedDataRef.value.tableData.length === 0) {
    ElMessage({ showClose: true, message: '文件为空，请重新上传。', type: 'error' });
    return; 
  }
  let hashs = resolvedDataRef.value.tableData.map(item => item.isfdLargeFileVo.fileHash);
  files.value = files.value.filter(f => hashs.includes(f.fileHash));
  let params = JSON.parse(JSON.stringify(uploadForm.value));
  params.sectionNumbers = params.sectionNumbers.join(',');
  params.largeFileBoList = files.value;
  confirmOp2Upload(params).then(res => {
    if (res.data && res.data.length > 0) {
      res.data.forEach(item => {
        const { uploadId, existChunks, totalChunks, chunkIndex, fileUrl } = item;
        let fItem = files.value.find(file => file.fileHash == item.fileHash);
        if (fItem) {
          fItem.uploadId = uploadId;
          fItem.existChunks = existChunks;
          fItem.totalChunks = totalChunks;
          fItem.chunkIndex = chunkIndex;
          fItem.fileUrl = fileUrl;
          // fItem.chunks = createChunks(totalChunks);
          fItem.progress = 0;
          fItem.uploadedChunks = new Set(existChunks || []);
        }
      })
      if (res.data.every(item => !!item.fileUrl)) {
        active.value ++;
        return;
      }
      dialogFormVisible.value = true;
      handleFilesSelect();
    }
  })
}

// 所有切片上传成功，跳转第三步完成页面
const handleFilesSelect = async () => {
  // for (const file of files) {
  //   try {
  //     startUploadToWorker(file)
  //   } catch (error) {
  //     console.error('文件处理失败:', error)
  //     removeFile(fileData)
  //   }
  // }
  for (const file of files.value) {
    if (file.fileUrl) {
      file.chunks = [];
    } else {
      const chunks = Array.from({ length: Math.ceil(file.file.size / CHUNK_SIZE) }, (_, i) =>
        file.file.raw.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE)
      )
      file.chunks = chunks;
    }
  }
  // for (const file of filesForWorker) {
  //   const chunks = Array.from({ length: Math.ceil(file.file.size / CHUNK_SIZE) }, (_, i) =>
  //     file.file.raw.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE)
  //   )
  //   file.chunks = chunks;
  // }
  startUpload();
}
/*********worker进程********/
// 生成文件分片
function createChunks(size) {
  const chunks = []
  for (let i = 0; i < Math.ceil(size / CHUNK_SIZE); i++) {
    chunks.push({
      index: i,
      start: i * CHUNK_SIZE,
      end: Math.min((i + 1) * CHUNK_SIZE, size)
    })
  }
  return chunks
}
// 全部上传成功跳转完成
const checkAllFileUploadSuccess = () => {
  return files.value.every(file => file.fileUrl || (file.totalChunks == file.uploadedChunks.size))
}
// 启动上传
const startUpload = () => {
  // uploadLoading.value = true
  // 计算所有文件的分片个数
  // files 是前一步上传的多个文件集合
  files.value.forEach((file) => {
    if (!file.fileUrl) {
      const chunkCount = Math.ceil(file.fileSize / CHUNK_SIZE);  // 300 / 10
      const threadChunkCount = Math.ceil(chunkCount / THREAD_COUNT); // 30 / 4 每个进程操作切片个数 8 
      
      for (let i = 0; i < THREAD_COUNT; i ++) {
        // 开启一个线程任务 
        const worker = new Worker(new URL('./upload.worker.js', import.meta.url), {
          type: 'module'
        })
        let start = i * threadChunkCount;
        let end = Math.min(start + threadChunkCount, chunkCount);
        
        worker.postMessage({
          type: 'START_UPLOAD',
          payload: {
            file: toRaw(file.file), //toRaw(_file),
            fileHash: file.fileHash,
            start: start,
            chunks: file.chunks.slice(start, end).map(c => toRaw(c)),
            uploadId: file.uploadId,
            chunkSize: CHUNK_SIZE,
            uploadedChunks: Array.from(toRaw(file.uploadedChunks)),
            token: getToken()
          }
        })

        worker.onmessage = (e) => {
          const { type, payload } = e.data
          switch (type) {
            case 'PROGRESS':
              file.progress = 100 * Number(file.uploadedChunks.size || 0) / file.totalChunks;
              break
            case 'CHUNK_DONE':
              file.uploadedChunks.add(payload.index)
              file.progress = 100 * Number(file.uploadedChunks.size || 0) / file.totalChunks;
              if (checkAllFileUploadSuccess()) {
                active.value ++;
                dialogFormVisible.value = false;
              }
              break
            case 'COMPLETE':
              console.log('上传完成:', payload)
              worker.terminate()
              break
            case 'ERROR':
              console.error('上传错误:', payload)
              break
          }
        }
      }
    } else {
      file.progress = 100;
    }
  })
}

/**************************/

// 完成组建
const finish = () => {
  emits('handleEvent', { component: 'Op2List', type: 'finish', tableName: props.tableName });
};
// 取消
const cancel = () => {
  emits('handleEvent', { component: 'Op2List', type: 'cancel', tableName: props.tableName });
};
onMounted(() => {
  getModelList();
});
</script>
<style lang="scss" scoped>
@import './op2.scss';
.upload_check {
  width: 100%;
  height: calc(100vh - 245px);
  overflow-y: auto;
}
.upload_elements {
  padding: 10px;
  width: 100%;
  height: calc(100vh - 84px);
  display: flex;
  flex-direction: column;
  align-items: center;
  overflow: scroll;
  font-size: 12px;
  .steps {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 30px;
  }
  .upload {
    width: 800px;
    margin-top: 20px;
    .upload_content {
      width: 800px;
      // height: 475px;
      text-align: center;
      .upload_form {
        margin-top: 20px;
        display: flex;
        flex-direction: column;
        align-items: center;
      }
    }
  }
  .upload_list,
  .upload_success {
    margin-top: 10px;
    // overflow: scroll;
    // min-height: calc(100vh - 251px);
    width: 100%;
  }
  .model-batch {
    padding: 10px;
    label {
      font-size: 16px;
    }
    span {
      margin-right: 20px;
      font-size: 14px;
    }
  }
  .option_btn {
    margin-top: 20px;
  }
  .upload_success {
    margin-top: 10px;
    .upload_success_message {
      margin-bottom: 10px;
      display: flex;
      flex-direction: column;
      align-items: center;
    }
  }
  .finish {
    width: 600px;
    margin-top: 20px;
  }
  :deep(.el-step__title) {
    font-size: 12px;
  }
}

</style>